﻿using System;
using System.Net;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Configuration;
using System.Web.Services.Protocols;


namespace Commerce.Providers
{
    /// <summary>
    /// Jason Prothero, ProWorks Corporation - http://www.proworks.com/
    /// 
    /// 
    /// </summary>
    class PayMeNowPaymentProvider : PaymentProvider
    {
        public PayMeNowPaymentProvider()
	    {  }

        #region Passed-in Properties

        string url = "https://trans.merchantpartners.com/cgi-bin/process.cgi";
        string action = "ns_quicksale_cc";
        string accountid = "TEST0";
        string subid = "";

        #endregion


        #region Provider specific behaviors

        public override void Initialize(string name, NameValueCollection config)
        {

            // Verify that config isn't null
            if (config == null)
                throw new ArgumentNullException("config");

            // Assign the provider a default name if it doesn't have one
            if (String.IsNullOrEmpty(name))
            {
                config.Remove("name");
                config.Add("name",
                    "PayMeNowPaymentProvider");
            }
            // Add a default "description" attribute to config if the
            //  attribute doesn't exist or is empty
            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description",
                    "PayMeNow Payment Provider");
            }
            base.Initialize(name, config);

            url = config["url"].ToString();
            if (String.IsNullOrEmpty(accountid))
                throw new ProviderException("Empty PayMeNow url value");
            config.Remove("url");

            accountid = config["accountid"].ToString();
            if (String.IsNullOrEmpty(accountid))
                throw new ProviderException("Empty PayMeNow accountid value");
            config.Remove("accountid");

            action = config["action"].ToString();
            if (String.IsNullOrEmpty(action))
                throw new ProviderException("Empty PayMeNow action value");
            config.Remove("action");

            subid = config["subid"].ToString();
        }

        public override string Name
        {
            get
            {
                return null;
            }
        }
        #endregion


        #region PaymentProvider Methods

        public override Commerce.Common.Transaction Charge(Commerce.Common.Order order)
        {
            //throw new NotImplementedException();
            // charge the card and get the return data to see what happened
            Commerce.Common.Transaction trans = new Commerce.Common.Transaction();

            string chargeResponse = "";


            try
            {
                HttpRequestHandler postRequest = new HttpRequestHandler(this.url);

                chargeResponse = postRequest.Post(BuildParamString(order));

                if (chargeResponse.Contains("Status=Accepted"))
                {
                    trans.GatewayResponse = chargeResponse;
                    trans.TransactionNotes = ProcessSuccess(chargeResponse);
                    trans.AuthorizationCode = ProcessValue("authcode",chargeResponse);
                }

                if (chargeResponse.Contains("Status=Declined"))
                {
                    throw new Exception("Transaction was declined.  " + ProcessDecline(chargeResponse));
                }
            }
            catch (Exception e)
            {
                throw new Exception("Gateway Error - " + e.Message);
            }

            return trans;
        }

        public override string Refund(Commerce.Common.Order order)
        {
            throw new Exception("This method not enabled for PayMeNow Payment Provider");
        }

        #endregion

        private string BuildParamString(Commerce.Common.Order order)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            sb.AppendFormat("action={0}",action);
            sb.AppendFormat("&acctid={0}", accountid);
            if (subid.Length > 0)
            {
                sb.AppendFormat("&subid={0}", subid);
            }

            decimal dTotal = order.OrderTotal;
            int roundTo = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalDigits;
            dTotal = Math.Round(dTotal, roundTo);
            sb.AppendFormat("&amount={0}", dTotal);

            sb.AppendFormat("&ccname={0} {1}", order.BillingAddress.FirstName, order.BillingAddress.LastName);
            sb.AppendFormat("&ccnum={0}", order.CreditCardNumber);
            sb.AppendFormat("&expyear={0}",  order.CreditCardExpireYear);
            sb.AppendFormat("&expmon={0}", order.CreditCardExpireMonth);
            sb.AppendFormat("&cvv2={0}", order.CreditCardSecurityNumber);
            sb.AppendFormat("&ci_email={0}", order.Email);
            sb.AppendFormat("&ci_billaddr1={0}",  order.BillingAddress.Address1);
            sb.AppendFormat("&ci_billaddr2={0}", order.BillingAddress.Address2);
            sb.AppendFormat("&ci_billstate={0}", order.BillingAddress.StateOrRegion);
            sb.AppendFormat("&ci_billzip={0}", order.BillingAddress.Zip);
            sb.AppendFormat("&ci_billcountry={0}", order.BillingAddress.Country);
            sb.AppendFormat("&ci_billcity={0}", order.BillingAddress.City);
            sb.AppendFormat("&merchantordernumber={0}", order.OrderNumber);
            sb.AppendFormat("&ci_phone={0}", order.BillingAddress.Phone);
            sb.AppendFormat("&ci_memo={0}", order.OrderGUID);
            sb.AppendFormat("&ci_IP Address={0}", order.UserIP.ToString());

            return sb.ToString();
        }


        private static string ProcessValue(string code, string reply)
        {
            string value = "";

            System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex(String.Format("{0}=(.+)$", code), System.Text.RegularExpressions.RegexOptions.Multiline);

            var matches = re.Matches(reply);

            value = matches[0].Groups[1].Value;

            return value;
        }


        private static string ProcessSuccess(string reply)
        {
            System.Text.StringBuilder value = new System.Text.StringBuilder();

            value.AppendFormat("\nTransaction ID: {0}", ProcessValue("refcode", reply));
            value.AppendFormat("\nAuthorization Code: {0}", ProcessValue("authcode", reply));
            value.AppendFormat("\nCapture Request Time: {0}", DateTime.Now);

            return value.ToString();
        }


        private static string ProcessDecline(string reply)
        {
            System.Text.StringBuilder value = new System.Text.StringBuilder();

            string reasonStr = "";
            string messageStr = "";

            System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex("(.+)=(.+)$", System.Text.RegularExpressions.RegexOptions.Multiline);
            var matches = re.Matches(reply);

            if (matches.Count > 0)
            {
                if (matches[0].Length > 1)
                {
                    reasonStr = matches[0].Groups[1].Value;
                }
            }

            System.Text.RegularExpressions.Regex reMsg = new System.Text.RegularExpressions.Regex("Reason=DECLINED:\\d*:(.+):", System.Text.RegularExpressions.RegexOptions.Multiline);
            matches = reMsg.Matches(reply);

            if (matches.Count > 0)
            {
                if (matches[0].Length > 1)
                {
                    messageStr = matches[0].Groups[1].Value;
                }
            }

            int reasonCode = -1;
            if( Int32.TryParse(reasonStr, out reasonCode) )
            {
                switch (reasonCode)
                {
                    case 0:
                        value.AppendFormat("Authorizing Network declined the transaction. - {0}", messageStr);
                        break;
                    case 1:
                        value.AppendFormat("Gateway declined the transaction. - {0}", messageStr);
                        break;
                    case 2:
                        value.AppendFormat("Authorizing Network returned an error, forcing a decline. - {0}", messageStr);
                        break;
                    case 3:
                        value.AppendFormat("Gateway returned an error, forcing a decline. - {0}", messageStr);
                        break;
                    case -1:
                        value.AppendFormat("Gateway declined the transaction.");
                        break;
                }
            }

            return value.ToString();
        }


    }
}
